#ifndef PHYSICS_H
#define PHYSICS_H

#define PHYSICS (Physics::GetSingleton())

#include <string>
#include <vector>
#include "Vector2df.h"
#include "BaseUnit.h"
#include "BaseTrigger.h"
#include "MapData.h"
#include "CollisionObject.h"
#include "Camera.h"

//----------------------------
// by foxblock for Wandor - Oct 14 2009
// Global physics class to handle all collision
// Partially hacked together into a working state and very ugly because I lost my nerve
//----------------------------

class Physics {
    private:
        Physics();
        static Physics* m_physics;
    public:
        ~Physics();
        static Physics* GetSingleton();

        float getGravity() const {return m_gravity;}
        void setGravity(CRfloat newGrav) {m_gravity = newGrav;}
        float getFriction() const {return m_friction;}
        void setFriction(CRfloat newFric) {m_friction = newFric;}

        void calculatePhysics(StatefulUnit* const unit) const;
        void checkHeroCollision(StatefulUnit* const unit, StatefulUnit* const hero) const;
        void checkUnitCollision(StatefulUnit* const unit, vector< BaseUnit* >* const enemies) const;
        void checkTriggerCollision(BaseUnit* const unit, StatefulUnit* const hero) const;
        void checkMapCollision(StatefulUnit* const unit) const;

        // utility functions
        // return value is movement for next tick
        Vector2df hardCollision(Vector2df& speed, const Vector2df& correction, CRfloat bounceXoverride=0, CRfloat bounceYoverride=0);
        Vector2df softCollision(Vector2df& speed, const Vector2df& correction);
    private:
        void checkMapCollisionX(const StatefulUnit* const unit, const MapData* const data, CollisionObject &obj, bool returnNoColTiles = false) const;
        void checkMapCollisionY(const StatefulUnit* const unit, const MapData* const data, CollisionObject &obj, bool returnNoColTiles = false) const;

        float rectangular(CRfloat pos, CRint tilePos, CRfloat speed, CRint tileSize) const;
        float oneWay(CRfloat pos, CRint tilePos, CRfloat speed, CRint tileSize) const;
        float slantBottomRight(CRfloat posX, CRfloat posY, CRint tileX, CRint tileY, CRint tileSize, CRfloat ySpeed) const;
        float slantBottomLeft(CRfloat posX, CRfloat posY, CRint tileX, CRint tileY, CRint tileSize, CRfloat ySpeed) const;

        bool doubleCheck(const MapData* const data, const Vector2df& tile, const Vector2df& correction) const;

        float m_gravity; // positive values drag units to the bottom of the screen, negative values up
        float m_friction; // zero will result in no friction and no decelleration caused by the ground
};

#endif // PHYSICS_H
